#pragma
#include <sys/select.h>
 #include <poll.h>
#include "Sock.hpp"
#include <iostream>
static const int portdefault = 8888;
static const int fddefault = -1;
static const uint16_t eventdefault = 0;

// typedef int type_t;
// #define READ_EVENT 0x1
// #define WRITE_EVENT (0x1 << 1)
// #define EXCEPT_EVENT (0x1 << 2)
// typedef struct Events
// {
//     int fd;
//     uint16_t events;
//     std::string ip;
//     uint16_t port;
// } type_t;

typedef struct pollfd type_t;

class PollServer
{
public:
    PollServer(uint16_t port = portdefault) : _port(port)
    {
        N = 1024;
        _fdarry = new type_t[N];
    }

    ~PollServer()
    {
        _listensock.Close();
        if(_fdarry)
            delete[] _fdarry;
    }

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        for (int i = 0; i < N; i++)
        {
            _fdarry[i].fd = fddefault;
            _fdarry[i].events = eventdefault;
            _fdarry[i].revents = eventdefault;
        }
    }

    void Accepter()
    {
        std::string clientip;
        std::uint16_t clientport;
        int sock = _listensock.Accept(clientip, clientport);
        if (sock < 0)
            return;
        //  到这里我们已经可以获取到新的连接了。
        //  同样的我能直接recv吗？不行，你怎么知道这个新的连接事件已经就绪了呢？
        //  我们无法知道，只能告知select让它帮我们关心这个新的连接，当它有事件到来时帮我关心
        //  所以我们需要把新的sock设进事件集合中

        //  我们怎么给事件集合设置,通过返回值? 还是在嵌套一个select呢？
        //  不管是哪个，都不能忽略一个问题，事件集合，都是输入输出型的，每执行一次select，它都会重新设置事件集合
        //  你得保证你之前的sock，重新设置进事件集合中。
        //  不管是上述的哪种方案都不是可行的。
        //  换句话说，我们要对关心的 文件描述符做管理！你需要另外用一个结构来管理这些文件描述符！
        //  这里我们使用原生原味的selectServer，使用数组的形式来组织这些文件描述符！

        int pos = 1;
        for (; pos < N; pos++)
        {
            if (_fdarry[pos].fd == fddefault)
                break;
        }
        // 还需要做判断，pos是否合法
        if (pos >= N)
        {
                //  logMessage(Info, "_fdarry[] is full\n");
                //  可以做扩容管理
                _fdarry = (type_t*)realloc(_fdarry,sizeof(type_t) * N * 3 / 2);
                N = N * 3 / 2;
        }
        _fdarry[pos].fd = sock;
        // 当写到这里呢，还是有问题。
        // 我们这里只关心了读事件，如果还要关心写事件呢？？？
        // 当换了一个新的结构就可以关心 对应的事件了

        // 关注写事件一直刷屏我们这里就不关心了
        // _fdarry[pos].events = (POLLIN | POLLOUT);
        _fdarry[pos].events =  POLLIN ;
        _fdarry[pos].revents = eventdefault;
        logMessage(Info, "create a new sock %d, event = %d\n", sock, _fdarry[pos].events);
    }

    std::string Recver(int i)
    {
        int fd = _fdarry[i].fd;
        char buf[1024] = {0};
        int n = recv(fd, buf, sizeof(buf) - 1, 0);
        if (n > 0)
        {
            buf[n - 1] = 0;
            std::cout << buf << std::endl;

            std::string msg = "server: ";
            msg += buf;
            send(fd, msg.c_str(), msg.size(), 0);
        }
        else
        {
            if (n == 0)
            {
                // 读到了结束位置
                // 在网络中，只有对端关掉了连接，才会读到结束。
                logMessage(Info, " %d close connect\n", fd);
            }
            else
            {

                // 以阻塞形式读取数据，当n < 0 肯定时读取错误了。
                // 如果以非阻塞的形式读取，当 n < 0 时，存在2种情况，当 errno == EAGIN 表示数据还未准备好，需要重新读
                // 另一种情况是errno == EINTR，表示在读取时被信号中断了，导致读错误的，同样的也需要去重新读
                // 我们这里是非阻塞情况，所以我们就不考虑这两种特殊情况
                logMessage(Warning, "recv failed,close sock %d\n", fd);
            }
            // 同时需要把_fdarry[]中的fd置为fddefault
            _fdarry[i].fd = fddefault;
            _fdarry[i].events = eventdefault;
            _fdarry[i].revents = eventdefault;
            close(fd);
        }
        return buf;
    }
    
    void Sender(std::string buf,int i)
    {
        int fd = _fdarry[i].fd;
        std::string msg = "server: ";
        msg += buf;
        send(fd, msg.c_str(), msg.size(), 0);
    }

    void HandlerEvents()
    {
        // 现在读事件就绪，如果在使用accept会被阻塞吗？
        // 并不会！当程序执行到这里时，select已经告知我有事件就绪了
        std::string buf;
        for (int i = 0; i < N; i++)
        {
            if (_fdarry[i].fd == fddefault)
                continue;
            int fd = _fdarry[i].fd;
            uint16_t revent = _fdarry[i].revents;
            if (fd == _listensock.Fd() && (revent & POLLIN))
            {
                Accepter();

            } // 如果不是监听套接字，而是其他套接字。当其他套接字，读事件就绪时，正常的处理读即可
            else if (fd != _listensock.Fd() && (revent & POLLIN))
            // else if (fd != _listensock.Fd() && READ_EVENT & event)
            {
                printf("Handler ReadEvent fd = %d\n",fd);
                buf = Recver(i);
            }
            else if (fd != _listensock.Fd() && (revent & POLLOUT))
            {
                // 一般呢，读写事件不并发处理，并发只要把else if改为if即可。    
                // 一般不都是读了数据之后还要对数据进行处理，处理完成之后在写回去。
                Sender(buf,i);
            }
        }
    }
    // 服务器是个死循环
    void ShowFdarry()
    {
        std::cout << "关心的文件描述符：";
        for (int i = 0; i < N; i++)
        {
            if (_fdarry[i].fd == fddefault)
                continue;
            std::cout << _fdarry[i].fd << " ";
        }
        std::cout << std::endl;
    }

    void Start()
    {
        // POLLIN  -- 读事件
        // POLLOUT -- 写事件
        _fdarry[0].fd = _listensock.Fd();
        _fdarry[0].events = POLLIN;
        while (true)
        {
            // int poll(struct pollfd *fds, nfds_t nfds, int timeout);
            // fds：是用户自己维护的一个动态变化的数组
            // nfds：动态数组的太小
            // timeout：0 是非阻塞，-1阻塞， m>0,在m秒内阻塞，超过m秒非阻塞返回,单位是微秒
            // poll中的 struct pollfd 其实和我们在select中自定义的结构体很像，本质上是一样的！
            // 因为poll就是 以前写selectServer的程序员们，优化而来的
            // struct pollfd 
            // {
            //    int   fd;         /* file descriptor */
            //    short events;     /* requested events */
            //    short revents;    /* returned events */
            // };
            // fd：关心的文件描述符
            // events：用户告诉内核要关心，fd上的哪些事件
            // revents：内核告诉用户，fd上有哪些事件就绪
            ShowFdarry();
            int n = poll(_fdarry, N, 2000);
           
            switch (n)
            {

            case 0:
                // 无事件就绪
                logMessage(Debug, "no fd is ready\n");
                break;
            case -1:
                // select出错
                logMessage(Warning, "select failed %d-%s\n", errno, strerror(errno));
                break;
            default:
                // 说明已经有事件就绪了
                // 处理事件
                logMessage(Info, "有%d个事件已经就绪了\n", n);
                HandlerEvents();
                break;
            }
            // sleep(1);
        }
    }

private:
    Sock _listensock;
    uint16_t _port;
    // 要管理的文件描述符
    type_t* _fdarry;
    int N;
};